#include "comm_monitor.h"
#include "comm_monitor_callback.h"
#include <unistd.h>
#include <sys/time.h>
#include "debug.h"
#include "msg_queue_interface.h"
#include "msg_queue_pid_define.h"
#include "function_interface.h"
#include <pthread.h>
#include <unistd.h>
#include <sys/syscall.h>
#include "misc.h"
#include "Commom/timer_interface.h"
#include "syslog.h"
#include "rated_limit.h"


typedef struct 
{
    commState_t commState;      /* 通讯状态 */
    commMonitorEn_t monitorEn;  /* 监控使能 */
    uint32_t timeToOnline;      /* 在线判定时间 */
    uint32_t secCntOnline;      /* 在线秒数 */
    uint32_t timeToOffline;     /* 离线判定时间 */
    uint32_t secCntOffline;     /* 离线秒数 */
    uint32_t heartbeatCur;      /* 当前心跳 */
    uint32_t heartbeatLast;     /* 上次心跳 */
} commMonitor_t;


static commMonitor_t commMonitor[COMM_DEVICE_QTY] = {0};


int CommMonitor_AddHeartbeat(commDevice_t dev)
{
    if(dev >= COMM_DEVICE_QTY)
    {
        PRINT_WARN();
        return -1;
    }

    commMonitor[dev].heartbeatCur++;
    // PRINT_DEBUG("dev[%d]", dev);
    return 0;
}


int CommMonitor_GetCommState(commDevice_t dev, commState_t *pState)
{
    if(dev >= COMM_DEVICE_QTY || pState == NULL)
    {
        PRINT_WARN();
        return -1;
    }

    *pState = commMonitor[dev].commState;
    return 0;
}


int CommMonitor_SetMonitorSwitch(commDevice_t dev, commMonitorEn_t en)
{
    if(dev >= COMM_DEVICE_QTY)
    {
        PRINT_WARN();
        return -1;
    }

    commMonitor[dev].monitorEn = en;

    if(en == COMM_MONITOR_DISABLE)
    {
        commMonitor[dev].commState = COMM_STATE_INIT;
        commMonitor[dev].secCntOnline = 0;
        commMonitor[dev].secCntOffline = 0;
        commMonitor[dev].heartbeatCur = 0;
        commMonitor[dev].heartbeatLast = 0;
        RatedLimit_SetPercentage((ratedLimitOwner_t)(dev + 16), 1.0F);
    }
    
    return 0;
}


int CommMonitor_ConfigTime(commDevice_t dev, uint32_t timeToOnline, uint32_t timeToOffline)
{
    if(dev >= COMM_DEVICE_QTY)
    {
        PRINT_WARN();
        return -1;
    }

    commMonitor[dev].timeToOnline = timeToOnline;
    commMonitor[dev].timeToOffline = timeToOffline;
    
    return 0;
}


static int CommMonitor_Init(void)
{
    uint16_t commAll, idxDev;
    
    for(idxDev = 0; idxDev < COMM_DEVICE_QTY; idxDev++)
    {
        if(idxDev < 8)
        {
            CommMonitor_ConfigTime(idxDev, 3, 30);
        }
        else
        {
            CommMonitor_ConfigTime(idxDev, 3, 120);
        }
    }

    if(FuncInf_GetCommunicateSwitch(FUNCINF_COMM_SWITCH_TYPE_ALL, &commAll) != FUNCINF_ERR_NONE)
    {
        PRINT_WARN();
        return -1;
    }

    if(FuncInf_SetCommunicateSwitch(FUNCINF_OPERATOR_INIT, FUNCINF_COMM_SWITCH_TYPE_ALL, commAll) != FUNCINF_ERR_NONE)
    {
        PRINT_WARN();
        return -1;
    }

    CommMonitor_SendMessage();

    return 0;
}


static timer_t timerIdCommMonitor = NULL;


static void CommMonitor_Handle(union sigval arg)
{
    commMonitor_t *pCommMonitor = NULL;
    ((void)(arg));
    PRINT_DEBUG();

    uint16_t idxDev, testAlone;

    if(FuncInf_GetTestSwitch(FUNCINF_TEST_SWITCH_TYPE_ALONE_TEST, &testAlone) != FUNCINF_ERR_NONE)
    {
        PRINT_WARN();
    }
    
    for(idxDev = 0; idxDev < COMM_DEVICE_QTY; idxDev++)
    {
        pCommMonitor = &commMonitor[idxDev];

        if(pCommMonitor->monitorEn == COMM_MONITOR_DISABLE)
        {
            continue;
        }

        if(testAlone != 0)
        {
            CommMonitor_AddHeartbeat(idxDev);
        }

        PRINT_DEBUG("idxDev[%d] hbCur[%d] hbLast[%d]", idxDev, pCommMonitor->heartbeatCur, pCommMonitor->heartbeatLast);

        if(pCommMonitor->heartbeatCur == pCommMonitor->heartbeatLast)
        {
            pCommMonitor->secCntOnline = 0;
            if(pCommMonitor->commState != COMM_STATE_OFFLINE)
            {
                pCommMonitor->secCntOffline++;
            }
            
            if(pCommMonitor->secCntOffline == pCommMonitor->timeToOffline)
            {
                if(pCommMonitor->commState == COMM_STATE_INIT)
                {
                    CommMonitorCallback_InitToOffline(idxDev);
                }
                if(pCommMonitor->commState == COMM_STATE_ONLINE)
                {
                    CommMonitorCallback_OnlineToOffline(idxDev);
                }

                pCommMonitor->commState = COMM_STATE_OFFLINE;
            }
        }
        else
        {
            pCommMonitor->secCntOffline = 0;
            if(pCommMonitor->commState != COMM_STATE_ONLINE)
            {
                pCommMonitor->secCntOnline++;
            }

            if(pCommMonitor->secCntOnline == pCommMonitor->timeToOnline)
            {
                if(pCommMonitor->commState == COMM_STATE_INIT)
                {
                    CommMonitorCallback_InitToOnline(idxDev);
                }
                if(pCommMonitor->commState == COMM_STATE_OFFLINE)
                {
                    CommMonitorCallback_OfflineToOnline(idxDev);
                }

                pCommMonitor->commState = COMM_STATE_ONLINE;
            }
        }

        pCommMonitor->heartbeatLast = pCommMonitor->heartbeatCur;
    }
}


static int CommMonitor_Run(time_t sec, long nsec)
{
    if(TimerInf_Create(&timerIdCommMonitor, CommMonitor_Handle, NULL) != 0)
    {
        PRINT_WARN();
        return -1;
    }

    if(TimerInf_Start(timerIdCommMonitor, sec, nsec, TIMER_CYCLE) != 0)
    {
        PRINT_WARN();
        return -2;
    }
    
    syslog(LOG_INFO, "%s: CommMonitor Run", __func__);
    return 0;
}


static int CommMonitor_Stop(void)
{
    if(TimerInf_Delete(&timerIdCommMonitor) != 0)
    {
        PRINT_WARN();
        return -1;
    }

    // syslog(LOG_INFO, "%s: CommMonitor Stop", __func__);
    return 0;
}


void CommMonitor_Thread(void *pParam)
{    
    Misc_InitThread(__func__);

    uint16_t commAll = 0;
    int err, msgId;
    char msgData[MSG_DATA_LEN] = {0};

    if(MsgQueue_Get(MSG_QUEUE_PID_COMM_MONITOR, &msgId) < 0)
	{
        // PRINT_ERROR("Get error");
		return;
	}

    CommMonitor_Init();

    while(1)
    {
        if((err = MsgQueue_Recv1(msgId, msgData)) < 0)
        {
            // PRINT_ERROR("Recv error[%d]", err);
        }
        // PRINT_DEBUG();
        
        if(FuncInf_GetCommunicateSwitch(FUNCINF_COMM_SWITCH_TYPE_ALL, &commAll) != FUNCINF_ERR_NONE)
        {
            PRINT_WARN();
        }

        /* 通讯监测开启 */
        if(commAll)
        {
            CommMonitor_Run(1, 0);
        }
        else
        {
            CommMonitor_Stop();
        }
    }
}


int CommMonitor_SendMessage(void)
{
    int err, msgId;
    char buf[MSG_DATA_LEN] = {0};

    if((err = MsgQueue_Get(MSG_QUEUE_PID_COMM_MONITOR, &msgId)) < 0)
	{
        // PRINT_WARN("err[%d]", err);
		return -1;
	}

    if((err = MsgQueue_Send1(msgId, buf)) < 0)
    {
        PRINT_WARN("err[%d]", err);
        return -2;
    }

    return 0;
}


int CommMonitor_DeleteTimer(void)
{
    if(TimerInf_Delete(&timerIdCommMonitor) != 0)
    {
        PRINT_WARN();
        return -1;
    }

    return 0;
}


int CommMonitor_GetTimerId(void)
{
    return (int)timerIdCommMonitor;
}

